package conf2.model;

import conf2.model.EActor.Side;
import conf2.util.Arr;
import java.util.LinkedList;
import java.util.Queue;

public class MoveLogic
{

    public static boolean isValidMove(WorldState ws, EActor who, ETerritory to)
    {
        ETerritory from = ws.whereIs(who);
        assert from != null;

        if (to == null) {
            return false;
        }

        switch (who) {
            case C_ARAGORN:
                if (ws.isOccupiedBy(to, Side.DARK) && WorldMap.isAdjacentTo(from, to)) {
                    return true;
                }
                break;
            case C_WITCHKING:
                if (ws.isOccupiedBy(to, Side.LIGHT) && WorldMap.isSidewaysTo(from, to)) {
                    return true;
                }
                break;
            case C_F_NAZGUL:
                if (ws.isOccupiedBy(to, Side.LIGHT) && ws.getOccupantCount(to) == 1) {
                    return true;
                }
                break;
            case C_B_RIDERS:
                return isValidRiderMove(ws, from, to);
            case V_URUKHAI:
                return isValidUrukHaiMove(ws, from, to);
            case V_TREEBEARD:
                if (to == ETerritory.FANGORN &&
                        ws.isOccupiedBy(to, Side.DARK) &&
                        ws.getOccupantCount(to) == 1) {
                    return true;
                }
                break;
            case V_F_NAZGUL:
                return isValidVariantNazgulMove(ws, from, to);
            case V_WATCHER:
                if (ws.isWatcherRevealed()) {
                    return false;
                }
                break;
            default:
                break;
        }

        return isValidRegularMove(ws, who, to);
    }

    private static boolean isValidRegularMove(WorldState ws, EActor who, ETerritory to)
    {
        ETerritory from = ws.whereIs(who);
        ETerritory[] possibleMoves = (who.isLight() ? WorldMap.getLightForward(from) : WorldMap.getDarkForward(from));
        boolean canMoveTo = Arr.contains(to, possibleMoves);
        boolean friendlyAndFree = ws.getOccupyingSide(to) == who.getSide() && !ws.isFull(to);
        boolean unfriendly = ws.getOccupyingSide(to) != who.getSide();

        return canMoveTo && (friendlyAndFree || unfriendly);
    }

    private static boolean isValidRiderMove(WorldState ws, ETerritory from, ETerritory to)
    {
        Queue<ETerritory> tqueue = new LinkedList<ETerritory>();

        for (ETerritory darkNeighbor : WorldMap.getDarkForward(from)) {
            tqueue.add(darkNeighbor);
        }

        while (!tqueue.isEmpty()) {

            ETerritory curr = tqueue.poll();

            boolean impassable =
                    ws.isOccupiedBy(curr, Side.LIGHT) ||
                    ws.isFull(curr);
            if (impassable) {
                continue;
            }

            // Breadth-first expansion
            for (ETerritory candidate : WorldMap.getDarkForward(curr)) {

                if (candidate == to) {
                    return true;
                }

                if (!tqueue.contains(candidate)) {
                    tqueue.add(candidate);
                }
            }
        }
        return false;
    }

    private static boolean isValidUrukHaiMove(WorldState ws, ETerritory from, ETerritory to)
    {
        boolean neighbor = WorldMap.isDarkForwardTo(from, to);
        if (neighbor && !(ws.isFull(to) && ws.isOccupiedBy(to, Side.DARK))) {
            return true;
        }

        if (ws.isOccupiedBy(to, Side.LIGHT)) {
            return false;
        }

        return ws.isEmpty(to) && isValidRiderMove(ws, from, to);

    }

    private static boolean isValidVariantNazgulMove(WorldState ws, ETerritory from, ETerritory to)
    {
        boolean neighbor = WorldMap.isDarkForwardTo(from, to);
        if (neighbor && !(ws.isFull(to) && ws.isOccupiedBy(to, Side.DARK))) {
            return true;
        }

        for (ETerritory t : WorldMap.getDarkForward(from)) {
            for (ETerritory u : WorldMap.getDarkForward(t)) {
                if (u == to && ws.isOccupiedBy(u, Side.LIGHT)) {
                    return true;
                }
            }
        }
        return false;
    }
}
